package com.bizmda.bizsip.source;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.bizmda.bizsip.common.*;
import com.bizmda.bizsip.config.*;
import com.bizmda.bizsip.converter.AbstractConverter;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.lang.reflect.InvocationTargetException;
import java.util.List;

/**
 * @author 史正烨
 */
@Slf4j
@Service
@Scope("prototype")
@Getter
public class Source {
    @Value("${bizsip.config-path:#{null}}")
    private String configPath;
    @Value("${bizsip.integrator-url}")
    private String integratorUrl;
//    @Value("${spring.cloud.nacos.discovery.server-addr}")
//    private String serverAddr;

    @Autowired
    private SourceConfigMapping sourceConfigMapping;

    @Autowired
    private RestTemplate restTemplate;

    private String sourceId;

    private AbstractConverter converter;
    private List<PredicateRuleConfig> serviceRules;

    /**
     * 客户端适配器初始化，主要包括：
     * 1.初始化消息处理器，装载消息转换配置
     * 2.装载聚合服务定位断言规则
     * @param sourceId 客户端适配器Id，在client-adaptor.yml文件中定义
     * @throws BizException
     */
    @Deprecated
    public void init(String sourceId) throws BizException {
        this.sourceId = sourceId;
        this.load();
        log.info("Source端配置读入成功!");
    }

    private void load() throws BizException {
        CommonSourceConfig sourceConfig = this.sourceConfigMapping.getSourceId(this.sourceId);
        if (sourceConfig == null) {
            throw new BizException(BizResultEnum.SOURCE_ID_NOTFOUND,"source:"+this.sourceId);
        }
        if (sourceConfig.getConverterMap() == null) {
            this.converter = null;
        }
        else {
            String converterType = (String) sourceConfig.getConverterMap().get("type");
            Class<? extends AbstractConverter> clazz = (Class<? extends AbstractConverter>) AbstractConverter.CONVERTER_TYPE_MAP.get(converterType);
            if (clazz == null) {
                throw new BizException(BizResultEnum.CONVERTOR_NOT_SET);
            }

            try {
                this.converter = clazz.getDeclaredConstructor().newInstance();
            } catch (InstantiationException | IllegalAccessException
                    | InvocationTargetException | NoSuchMethodException e) {
                throw new BizException(BizResultEnum.CONVERTOR_CREATE_ERROR, e);
            }

            this.converter.init(this.configPath, sourceConfig.getConverterMap());
        }
        this.serviceRules = sourceConfig.getServiceRules();
    }

    /**
     * 客户端适配器在收到消息后，执行的处理动作，依次为：
     * 1.消息解包
     * 2.根据解包消息执行聚合服务定位断言规则，定位聚合服务id
     * 3.发送消息给聚合整合器处理
     * 4.消息打包
     * @param inMessage
     * @return
     * @throws BizException
     */
    @Deprecated
    public BizMessage<byte[]> process(byte[] inMessage) throws BizException {
        log.trace("Source传入消息:\n{}",BizUtils.buildHexLog(inMessage));
        log.debug("Source调用Convert[{}]解包",this.converter.getType());
        JSONObject message = this.converter.unpack(inMessage);
        log.trace("Source解包后消息:\n{}",BizUtils.buildJsonLog(message));
        log.debug("Source调用Integrator服务");
        BizMessage<JSONObject> bizMessage = this.doBizService(message);
        log.trace("Integrator返回消息:\n{}", BizUtils.buildJsonLog(bizMessage.getData()));
        log.debug("Source调用Convert[{}]打包",this.converter.getType());
        byte[] outMessage = this.converter.pack(bizMessage.getData());
        log.trace("Source打包后消息:\n{}",BizUtils.buildHexLog(outMessage));
        BizMessage<byte[]> outBizMessage = BizMessage.buildSuccessMessage(bizMessage,outMessage);
        log.trace("Source.process()返回:\n{}",BizUtils.buildBizMessageLog(outBizMessage));
        return outBizMessage;
    }

    private BizMessage<JSONObject> doBizService(JSONObject inData) throws BizException {
        String rule = this.matchServicePredicateRule(inData);
        if (this.integratorUrl.endsWith("/")) {
            this.integratorUrl = this.integratorUrl.substring(0,integratorUrl.length()-1);
        }
        HttpHeaders header = new HttpHeaders();
        header.add("Biz-Service-Id",rule);
        HttpEntity<JSONObject> httpEntity = new HttpEntity<>(inData, header);

        BizMessage<Object> outMessage = this.restTemplate.postForObject(this.integratorUrl, httpEntity, BizMessage.class);
        if (outMessage == null) {
            log.debug("Integrator返回为null");
            throw new BizException(BizResultEnum.SOURCE_RETURN_NULL);
        }
        if (outMessage.getCode() == 0) {
            log.debug("Integrator返回成功:{}",outMessage.getData());
        }
        else {
            log.debug("Integrator返回错误:{}-{}",outMessage.getCode(),outMessage.getMessage());
            throw new BizException(outMessage.getCode(),outMessage.getMessage());
        }
        return BizMessage.buildSuccessMessage(outMessage,JSONUtil.parseObj(outMessage.getData()));
    }

    private String matchServicePredicateRule(JSONObject inData) throws BizException {
        for (PredicateRuleConfig predicateRuleConfig:this.serviceRules) {
            if (predicateRuleConfig.getPredicate() == null ||
                    predicateRuleConfig.getPredicate().isEmpty()) {
                return BizUtils.getElStringResult(predicateRuleConfig.getRule(),inData);
            }
            boolean predicateFlag = BizUtils.getElBooleanResult(predicateRuleConfig.getPredicate(),inData);
            if (predicateFlag) {
                return BizUtils.getElStringResult(predicateRuleConfig.getRule(),inData);
            }
        }
        throw new BizException(BizResultEnum.OTHER_NO_MATCH_SERVICE_RULE);
    }
}
